// Compile:
// x86-64:
// gcc -Ofast -DRULES -m64 -g -Wall -msse2 range_sse2.c -o range_sse2
// x86:
// gcc -m64 -Ofast -g -Wall range_sse2.c -o range_sse2

// Request for single Range
// Range Server:   ./range | ncat -l -p 8081
// Range Receiver: ncat -i 500 127.0.0.1 8081 > state

// Statistics //

// Simple SIMD
// 16  * 2^32 takes ~6m29.115s produces ~235578 candidates
// 240 * 2^32 takes ~22m55.164s

// Fast SIMD GLFSR (-O2)
// 16 * 2^32 takes ~7m45.501s

// Fast SIMD GLFSR (-Ofast)
// 16 * 2^32 takes ~6m58.608s


#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

// SSE2
#include <emmintrin.h>

//#define DEBUG

#define RANGE 0x00000001000000000ULL // 16*2^32 

static   __m128i LFSR;
static   __m128i MASK;
static   __m128i ONE;
static   __m128i ONES;
static   __m128i LSB;



uint64_t _mask[2]  __attribute__((aligned(16))) = { 0x0, 0x800000000000000DULL };
uint64_t _value[2] __attribute__((aligned(16))) = { 0x0, 0x8 };
uint64_t _out[2]   __attribute__((aligned(16)));
uint64_t _one[2] __attribute__((aligned(16))) = { 0x0, 0x1 };
uint64_t _ones[2] __attribute__((aligned(16))) = { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL };


uint64_t lfsr      = 0x0; // 17
uint64_t mylfsr    = 0x0;
uint64_t stop_lfsr = 0x0;
uint8_t  byte;
uint64_t counter   = 0;
char 	 *p; 
int 	 Decimal_Rule = 0;
int 	 Capital_Rule = 0; 
int      Range_Rule  = 0;
int 	 i;
FILE     * pFile;

void ex_program(int sig);
void save_state();
void lfsr2string(uint64_t Lfsr, char *dst);
int  inRange(uint8_t byte);

char pass[9];

int  main()
{
  
  char    next_state [256];

  pFile = fopen ("get_state","r");
 
  if (pFile == NULL)
  { 
   printf ("Error opening file\n");
   return 1;
  }
   else
 {
     if ( fgets (next_state , 256 , pFile) != NULL )

#ifdef DEBUG
       puts (next_state);
#endif

      fclose (pFile);
   }

(void) signal(SIGINT, ex_program);

 for (i = 0; i < 8; i++)
 { 
       mylfsr += next_state[i];
       if (i<7) mylfsr <<= 8;
 }

 for (i = 8; i < 17; i++)
 { 
       stop_lfsr += next_state[i];
       if (i<16) stop_lfsr <<= 8;
 }

 lfsr = stop_lfsr;   

#ifdef DEBUG
 printf("last:\nstart at: %" PRIx64 "\n", mylfsr);
 printf("stop  at: %" PRIx64 "\n", stop_lfsr);
#endif

  _value[1] = lfsr;

  LFSR  = _mm_load_si128((__m128i *) _value);
  
  MASK  = _mm_load_si128((__m128i *) _mask);

  ONE   = _mm_load_si128((__m128i *) _one);

  ONES  = _mm_load_si128((__m128i *) _ones);

  _mm_store_si128((__m128i *) _out, LFSR);

#ifdef DEBUG
  printf("LFSR is %" PRIx64 "\n", _out[1]);
  printf("MASK is %" PRIx64 "\n", _mask[1]);
#endif

do {
/*
  lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0x800000000000000DULL);    

  This is buggy!?
  LFSR = _mm_xor_si128(_mm_srli_epi64 (LFSR, 1), _mm_and_si128(MASK, _mm_andnot_si128(_mm_and_si128(LFSR,ONE),ONES)));
*/
    
  LSB  = _mm_and_si128 (LFSR, ONE); // lfsr  &  1
  LFSR = _mm_srli_epi64 (LFSR, 1);  // lfsr >>  1

  _mm_store_si128((__m128i *) _out, LSB);
  if (_out[1]) // if lfsr(lsb) == 1
    LFSR = _mm_xor_si128 (MASK, LFSR); // lfsr ^ 0x800000000000000DULL

} while(counter++ != RANGE); // 16*2^32

 for(;;)
 {
   _mm_store_si128((__m128i *) _out, LFSR);
   lfsr = _out[1];
   
   if (inRange((lfsr & 0xFF00000000000000ULL) >> 56)) {
   if (inRange((lfsr & 0x00FF000000000000ULL) >> 48)) {
   if (inRange((lfsr & 0x0000FF0000000000ULL) >> 40)) {
   if (inRange((lfsr & 0x000000FF00000000ULL) >> 32)) {
   if (inRange((lfsr & 0x00000000FF000000ULL) >> 24)) {
   if (inRange((lfsr & 0x0000000000FF0000ULL) >> 16)) {
   if (inRange((lfsr & 0x000000000000FF00ULL) >> 8)) {
   if (inRange((lfsr & 0x00000000000000FFULL)))
    { break; }}}}}}}}

  // lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0x800000000000000Du);
  LSB  = _mm_and_si128 (LFSR, ONE); // lfsr  &  1
  LFSR = _mm_srli_epi64 (LFSR, 1);  // lfsr >>  1

  _mm_store_si128((__m128i *) _out, LSB);
  if (_out[1]) // if lfsr(lsb) == 1
    LFSR = _mm_xor_si128 (MASK, LFSR); // lfsr ^ 0x800000000000000DULL

 }

#ifdef DEBUG
 printf("next from\nstart at: %" PRIx64 "\n", stop_lfsr);
 printf("stop  at: %" PRIx64 "\n", lfsr);
#endif

 save_state();

 sleep(5); 
 fflush(stdout);

#ifdef RECURSIVE
  lfsr      = 0x0;
  mylfsr    = 0x0;
  stop_lfsr = 0x0;
  counter   = 0;

  main ();
#endif

return 0;
}


void ex_program(int sig) {
 printf("Stoped at: %" PRIx64 "\n", _out[1]);
 (void) signal(SIGINT, SIG_DFL);
 exit(0);
}

void lfsr2string(uint64_t Lfsr, char dts[9])
{
  dts[0] = (Lfsr & 0xFF00000000000000ULL) >> 56;
  dts[1] = (Lfsr & 0x00FF000000000000ULL) >> 48;
  dts[2] = (Lfsr & 0x0000FF0000000000ULL) >> 40;
  dts[3] = (Lfsr & 0x000000FF00000000ULL) >> 32;
  dts[4] = (Lfsr & 0x00000000FF000000ULL) >> 24;	
  dts[5] = (Lfsr & 0x0000000000FF0000ULL) >> 16;
  dts[6] = (Lfsr & 0x000000000000FF00ULL) >> 8;
  dts[7] = (Lfsr & 0x00000000000000FFULL);
  dts[8] = '\0';
}


void save_state() 
{
       char start_state[9];      
       char stop_state[9];      

       lfsr2string(stop_lfsr,      start_state);
       lfsr2string(lfsr,           stop_state);
       

       pFile = fopen ("get_state","w");
       fprintf (pFile, "%s %s\n", start_state, stop_state);	
       fclose (pFile);
       printf ("%s %s\n", start_state, stop_state);
}

inline int  inRange(uint8_t byte)
{
  if ((((byte >= 97) && (byte <= 122))) || (((byte >= 65) && (byte <= 90))) || (((byte >= 48) && (byte <= 57))))
	return 1;

  return 0;
}

